# core/note_manager.py - 笔记管理器
import json
import os
import shutil
from pathlib import Path
from typing import Dict, List, Optional, Callable
from cloudsync import CloudSync
import tkinter.messagebox as messagebox
import threading

from utils.crypto_utils import calculate_hash
from core.file_operations import FileOperations

class NoteManager:
    """笔记管理器 - 负责笔记的核心业务逻辑"""
    
    def __init__(self, config, root):
        self.config = config
        self.root = root
        self.cloud_sync = None
        self.files_index = {}
        self.current_file = None
        self.current_content = ""
        self.current_title = ""
        self.is_authenticated = False
        self.status_callback = None
        
        # 文件操作模块
        self.file_ops = FileOperations(config)
        
        # 加载本地缓存索引
        self.load_local_index()
    
    def set_status_callback(self, callback: Callable):
        """设置状态更新回调"""
        self.status_callback = callback
    
    def update_status(self, message: str):
        """更新状态"""
        if self.status_callback:
            self.status_callback(message)
        else:
            print(f"状态: {message}")
    
    def initialize_cloud_sync(self):
        """初始化CloudSync实例"""
        self.cloud_sync = CloudSync(self.config.webdav_url)
    
    # 认证相关方法
    def authenticate(self, email: str, password: str, webdav_url: str = None) -> bool:
        """认证用户"""
        if webdav_url:
            self.config.webdav_url = webdav_url
            self.initialize_cloud_sync()
        
        if not self.cloud_sync:
            self.initialize_cloud_sync()
            
        success = self.cloud_sync.authenticate(email, password)
        if success:
            self.is_authenticated = True
            self.config.save_config(email, password, webdav_url)
            self.ensure_remote_data_dir()
            self.show_info("认证成功！")
        else:
            self.is_authenticated = False
            self.show_error("认证失败，请检查邮箱和密码")
        return success
    
    def logout(self):
        """退出登录"""
        if self.is_authenticated and self.cloud_sync:
            self.cloud_sync.disconnect()
        self.is_authenticated = False
        self.config.email = ''
        self.config.password = ''
        self.config.save_config('', '')
        self.update_status("已退出登录")
    
    # 文件索引管理
    def load_local_index(self):
        """加载本地缓存的文件索引"""
        self.files_index = self.file_ops.load_local_index()
        self.update_status("已加载本地文件索引" if self.files_index.get("files") else "未找到本地文件索引")
    
    def save_local_index(self):
        """保存文件索引到本地缓存"""
        self.file_ops.save_local_index(self.files_index)
    
    def download_files_index(self, silent=False) -> bool:
        """下载最新的文件索引"""
        if not self.is_authenticated:
            if not silent:
                self.show_error("请先登录")
            return False
            
        try:
            if not silent:
                self.update_status("正在下载文件索引...")
            
            self.ensure_remote_data_dir()
            
            remote_index_path = f"{self.config.data_dir}/files.json"
            success = self.file_ops.download_remote_index(self.cloud_sync, remote_index_path)
            
            if success:
                # 更新本地索引
                self.files_index = self.file_ops.load_local_index()
                if not silent:
                    self.update_status("文件索引下载成功")
                return True
            else:
                if not silent:
                    self.update_status("远程没有文件索引，使用本地版本")
                return True
                
        except Exception as e:
            if not silent:
                self.show_error(f"下载文件索引失败: {str(e)}")
            return False
    
    def sync_files_index(self, callback=None, silent=False):
        """同步文件索引从WebDAV"""
        if not self.is_authenticated:
            if not silent:
                self.show_error("请先登录")
            if callback:
                callback(False)
            return
            
        try:
            if not silent:
                self.update_status("正在同步文件索引...")
            
            self.ensure_remote_data_dir()
            
            remote_index_path = f"{self.config.data_dir}/files.json"
            success = self.file_ops.download_remote_index(self.cloud_sync, remote_index_path)
            
            if success:
                # 更新本地索引
                self.files_index = self.file_ops.load_local_index()
                
                # 检查云端文件是否存在
                self.check_remote_files_exist(callback, silent)
                return
            else:
                # 如果远程没有files.json，创建一个空的并上传
                self.files_index = {"files": []}
                self.save_local_index()
                self.upload_files_index()
                if callback:
                    callback(True)
                return
                
        except Exception as e:
            if not silent:
                self.show_error(f"同步文件索引失败: {str(e)}")
            # 即使失败也创建一个空的索引
            self.files_index = {"files": []}
            self.save_local_index()
            if callback:
                callback(False)
    
    def check_remote_files_exist(self, callback=None, silent=False):
        """检查索引中的文件是否在云端存在"""
        missing_files = []
        
        for file_info in self.files_index.get("files", []):
            filename = file_info.get("name")
            if filename:
                remote_path = f"{self.config.data_dir}/{filename}"
                if not self.cloud_sync.file_exists(remote_path):
                    missing_files.append(file_info)
        
        if missing_files:
            file_list = "\n".join([f"- {f.get('title', f.get('name'))}" for f in missing_files])
            message = f"发现以下文件在云端不存在，是否从索引中移除？\n\n{file_list}"
            
            def handle_user_choice(user_choice):
                if user_choice:
                    self.files_index["files"] = [
                        f for f in self.files_index.get("files", []) 
                        if f not in missing_files
                    ]
                    
                    self.save_local_index()
                    
                    if self.upload_files_index():
                        self.show_info(f"已移除 {len(missing_files)} 个不存在的文件记录")
                    else:
                        self.show_error("移除文件记录失败")
                
                if callback:
                    callback(True)
            
            self.ask_question(message, handle_user_choice)
        else:
            if callback:
                callback(True)
    
    # 文件操作
    def get_file_info(self, filename: str) -> Optional[Dict]:
        """获取文件信息"""
        for file_info in self.files_index.get("files", []):
            if file_info.get("name") == filename:
                return file_info
        return None
    
    def load_file(self, filename: str, callback=None):
        """加载文件内容"""
        file_info = self.get_file_info(filename)
        if not file_info:
            if callback:
                callback("", "", False)
            return
        
        def check_and_load():
            cached_file = self.config.cache_dir / filename
            
            # 检查本地缓存
            if cached_file.exists():
                try:
                    with open(cached_file, 'r', encoding='utf-8') as f:
                        content = f.read()
                    
                    # 验证哈希
                    local_hash = calculate_hash(content)
                    expected_hash = file_info.get("hash", "")
                    
                    if local_hash == expected_hash:
                        title = file_info.get("title", "")
                        if callback:
                            callback(title, content, True)
                    else:
                        def handle_hash_mismatch(user_choice):
                            if user_choice:
                                self.download_and_load_file(filename, file_info, callback)
                            else:
                                title = file_info.get("title", "")
                                if callback:
                                    callback(title, content, False)
                        
                        message = f"文件 '{file_info.get('title', filename)}' 的哈希不匹配。\n\n可能是文件在云端已被修改。是否重新下载覆盖本地版本？"
                        self.ask_question(message, handle_hash_mismatch)
                        
                except Exception as e:
                    self.show_warning(f"读取本地缓存失败: {str(e)}")
                    self.download_and_load_file(filename, file_info, callback)
            else:
                self.download_and_load_file(filename, file_info, callback)
        
        threading.Thread(target=check_and_load, daemon=True).start()
    
    def download_and_load_file(self, filename: str, file_info: Dict, callback=None):
        """下载文件并加载内容"""
        if not self.is_authenticated:
            self.show_error("请先登录以下载文件")
            if callback:
                callback("", "", False)
            return
            
        content = self.download_file(filename)
        if content:
            local_hash = calculate_hash(content)
            hash_valid = local_hash == file_info.get("hash", "")
            title = file_info.get("title", "")
            if callback:
                callback(title, content, hash_valid)
        else:
            if callback:
                callback("", "", False)
    
    def download_file(self, filename: str) -> Optional[str]:
        """下载文件到本地缓存"""
        if not self.is_authenticated:
            self.show_error("请先登录")
            return None
            
        remote_path = f"{self.config.data_dir}/{filename}"
        local_path = self.config.cache_dir / filename
        
        try:
            self.update_status(f"正在下载文件: {filename}")
            success = self.cloud_sync.download_file(remote_path, str(local_path))
            if success and local_path.exists():
                with open(local_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                self.update_status(f"文件下载成功: {filename}")
                return content
            else:
                self.show_error(f"下载文件失败: {filename}")
                return None
        except Exception as e:
            self.show_error(f"下载文件异常: {str(e)}")
            return None

    def force_download_file(self, filename: str, callback=None):
        """强制下载文件（忽略本地缓存和哈希）"""
        if not self.is_authenticated:
            self.show_error("请先登录")
            if callback:
                callback("", "", False)
            return
            
        file_info = self.get_file_info(filename)
        if not file_info:
            if callback:
                callback("", "", False)
            return

        content = self.download_file(filename)
        if content:
            cached_file = self.config.cache_dir / filename
            with open(cached_file, 'w', encoding='utf-8') as f:
                f.write(content)
            
            new_hash = calculate_hash(content)
            hash_valid = new_hash == file_info.get("hash", "")
            title = file_info.get("title", "")
            
            if callback:
                callback(title, content, hash_valid)
        else:
            if callback:
                callback("", "", False)
    
    def upload_file(self, title: str, content: str) -> bool:
        """上传文件到WebDAV"""
        if not self.is_authenticated:
            self.show_error("请先登录")
            return False
            
        # 生成文件名
        import re
        filename = re.sub(r'[^\w\u4e00-\u9fa5]', '_', title) + ".txt"
        
        # 计算哈希和文件大小
        file_hash = calculate_hash(content)
        file_size = len(content.encode('utf-8'))
        
        try:
            # 先下载最新的文件索引
            if not self.download_files_index(silent=True):
                self.show_warning("无法获取最新文件索引，可能使用过期的索引")
            
            # 上传到WebDAV
            remote_path = f"{self.config.data_dir}/{filename}"
            self.update_status(f"正在上传文件: {filename}")
            
            # 使用文件操作模块上传
            success = self.file_ops.upload_file_content(
                self.cloud_sync, content, remote_path
            )
            
            if success:
                # 更新文件索引
                file_info = {
                    "name": filename,
                    "hash": file_hash,
                    "title": title,
                    "size": file_size,
                    "last_modified": ""
                }
                
                # 检查是否已存在
                existing_index = -1
                for i, info in enumerate(self.files_index.get("files", [])):
                    if info.get("name") == filename:
                        existing_index = i
                        break
                
                if existing_index >= 0:
                    self.files_index["files"][existing_index] = file_info
                else:
                    self.files_index["files"].append(file_info)
                
                # 保存索引
                self.save_local_index()
                self.upload_files_index()
                
                # 更新缓存
                cached_file = self.config.cache_dir / filename
                with open(cached_file, 'w', encoding='utf-8') as f:
                    f.write(content)
                
                self.update_status("文件上传成功")
                return True
            else:
                self.show_error("文件上传失败")
                return False
                
        except Exception as e:
            self.show_error(f"上传文件异常: {str(e)}")
            return False
    
    def upload_files_index(self) -> bool:
        """上传文件索引到WebDAV"""
        if not self.is_authenticated:
            self.show_error("请先登录")
            return False
            
        self.ensure_remote_data_dir()
        
        # 使用文件操作模块上传索引
        remote_path = f"{self.config.data_dir}/files.json"
        return self.file_ops.upload_files_index(self.cloud_sync, self.files_index, remote_path)
    
    def download_all_files(self, callback=None, index_updated_callback=None):
        """下载所有文件到本地缓存"""
        if not self.is_authenticated:
            self.show_error("请先登录")
            if callback:
                callback(False)
            return False
            
        # 先静默同步文件索引
        self.update_status("正在同步文件索引...")
        if not self.download_files_index(silent=True):
            self.show_error("无法获取最新文件索引")
            if callback:
                callback(False)
            return False
        
        # 索引同步完成后，立即调用回调更新UI
        if index_updated_callback:
            self.root.after(0, index_updated_callback)
        
        files = self.files_index.get("files", [])
        if not files:
            self.update_status("没有文件需要下载")
            if callback:
                callback(True)
            return True
            
        # 后台下载文件
        def download_files_async():
            downloaded_count = 0
            skipped_count = 0
            failed_files = []
            
            for file_info in files:
                filename = file_info.get("name")
                if not filename:
                    continue
                    
                expected_hash = file_info.get("hash", "")
                title = file_info.get("title", filename)
                
                # 检查本地文件是否存在且哈希匹配
                cached_file = self.config.cache_dir / filename
                if cached_file.exists():
                    try:
                        with open(cached_file, 'r', encoding='utf-8') as f:
                            content = f.read()
                        local_hash = calculate_hash(content)
                        
                        if local_hash == expected_hash:
                            skipped_count += 1
                            continue
                    except Exception:
                        pass
                
                # 需要下载文件
                self.update_status(f"正在下载: {title}")
                content = self.download_file(filename)
                if content:
                    downloaded_count += 1
                else:
                    failed_files.append(title)
            
            # 下载完成后验证哈希
            verification_failed = []
            for file_info in files:
                filename = file_info.get("name")
                if not filename:
                    continue
                    
                expected_hash = file_info.get("hash", "")
                title = file_info.get("title", filename)
                
                cached_file = self.config.cache_dir / filename
                if not cached_file.exists():
                    verification_failed.append(title)
                    continue
                    
                try:
                    with open(cached_file, 'r', encoding='utf-8') as f:
                        content = f.read()
                    local_hash = calculate_hash(content)
                    
                    if local_hash != expected_hash:
                        verification_failed.append(title)
                except Exception:
                    verification_failed.append(title)
            
            # 下载完成后，重新加载文件索引以确保UI更新
            self.load_local_index()
            
            # 显示结果
            if verification_failed:
                error_message = f"下载完成，但以下文件哈希验证失败，请手动检查：\n\n"
                error_message += "\n".join([f"- {name}" for name in verification_failed])
                self.show_error(error_message)
                if callback:
                    self.root.after(0, lambda: callback(False))
            else:
                success_message = f"所有文件下载完成！\n\n"
                success_message += f"已下载: {downloaded_count} 个文件\n"
                success_message += f"已跳过: {skipped_count} 个文件（哈希匹配）\n"
                success_message += f"总计: {len(files)} 个文件"
                self.show_info(success_message)
                if callback:
                    self.root.after(0, lambda: callback(True))
        
        threading.Thread(target=download_files_async, daemon=True).start()
        return True
    
    def search_files(self, keyword: str) -> List[Dict]:
        """搜索文件"""
        if not keyword:
            return self.files_index.get("files", [])
        
        results = []
        keyword_lower = keyword.lower()
        
        for file_info in self.files_index.get("files", []):
            filename = file_info.get("name", "").lower()
            title = file_info.get("title", "").lower()
            
            if keyword_lower in filename or keyword_lower in title:
                results.append(file_info)
        
        return results
    
    def delete_file(self, filename: str) -> bool:
        """删除文件"""
        if not self.is_authenticated:
            self.show_error("请先登录")
            return False
            
        try:
            # 先下载最新的文件索引
            if not self.download_files_index(silent=True):
                self.show_warning("无法获取最新文件索引，可能使用过期的索引")
            
            # 从云端删除文件
            remote_path = f"{self.config.data_dir}/{filename}"
            success = self.cloud_sync.delete_file(remote_path)
            
            if success:
                # 从索引中移除
                self.files_index["files"] = [
                    f for f in self.files_index.get("files", []) 
                    if f.get("name") != filename
                ]
                
                self.save_local_index()
                self.upload_files_index()
                
                # 删除本地缓存
                cached_file = self.config.cache_dir / filename
                if cached_file.exists():
                    cached_file.unlink()
                
                self.update_status(f"文件删除成功: {filename}")
                return True
            else:
                self.show_error(f"文件 '{filename}' 删除失败")
                return False
                
        except Exception as e:
            self.show_error(f"删除文件异常: {str(e)}")
            return False
    
    def rename_file(self, old_filename: str, new_title: str) -> bool:
        """重命名文件"""
        if not self.is_authenticated:
            self.show_error("请先登录")
            return False
            
        import re
        new_filename = re.sub(r'[^\w\u4e00-\u9fa5]', '_', new_title) + ".txt"
        
        if old_filename == new_filename:
            self.update_status("文件名未改变")
            return True
            
        try:
            if not self.download_files_index(silent=True):
                self.show_warning("无法获取最新文件索引，可能使用过期的索引")
            
            old_content = self.download_file(old_filename)
            if not old_content:
                self.show_error("无法下载原文件")
                return False
            
            success = self.upload_file(new_title, old_content)
            if not success:
                self.show_error("新文件上传失败")
                return False
            
            delete_success = self.delete_file(old_filename)
            if not delete_success:
                self.show_warning("新文件已创建，但原文件删除失败")
            
            self.update_status("文件重命名成功")
            return True
            
        except Exception as e:
            self.show_error(f"重命名文件异常: {str(e)}")
            return False
    
    def clear_local_cache(self):
        """清除本地所有缓存文件"""
        try:
            for item in self.config.cache_dir.iterdir():
                if item.is_file():
                    item.unlink()
                elif item.is_dir():
                    shutil.rmtree(item)
            
            self.files_index = {"files": []}
            self.save_local_index()
            
            self.show_info("本地缓存已清除")
            return True
        except Exception as e:
            self.show_error(f"清除缓存失败: {str(e)}")
            return False
    
    # 工具方法
    def ensure_remote_data_dir(self) -> bool:
        """确保远程data目录存在"""
        try:
            success = self.cloud_sync.create_directory(self.config.data_dir)
            return success
        except Exception as e:
            self.show_error(f"创建远程目录失败: {str(e)}")
            return False
    
    def show_info(self, message):
        """显示信息弹窗"""
        if len(message) > 100 or "\n" in message:
            self.root.after(0, lambda: messagebox.showinfo("信息", message))
        else:
            self.update_status(message)
    
    def show_warning(self, message):
        """显示警告弹窗"""
        if len(message) > 100 or "\n" in message:
            self.root.after(0, lambda: messagebox.showwarning("警告", message))
        else:
            self.update_status(f"警告: {message}")
    
    def show_error(self, message):
        """显示错误弹窗"""
        self.root.after(0, lambda: messagebox.showerror("错误", message))
    
    def ask_question(self, message, callback):
        """显示询问弹窗"""
        def show_dialog():
            result = messagebox.askyesno("确认", message)
            callback(result)
        self.root.after(0, show_dialog)